#include	"wyhash.h"
#include	<stdio.h>
#include	<math.h>
template<size_t	input,	size_t	hidden,	size_t	output>
class	tlfn{
private:
	const	float	alpha=1.704192254f,	wi=1/sqrtf(input+1),	wh=1/sqrtf(hidden);
	static	uint64_t	seed;
	float	weight[(input+1)*hidden+hidden*hidden+output*hidden],a[4*hidden+output];
	float	acti(float	x) {	return  alpha*x/sqrtf(1+x*x);	}
	float	grad(float	x) {	x=alpha*alpha-x*x;	return	x*sqrtf(x)/alpha;	}
public:
	tlfn(){	for(size_t	i=0;	i<sizeof(weight)/sizeof(float);	i++)	weight[i]=wy2gau(wyrand(&seed));	}	
	bool	save(FILE	*f){
		uint32_t	n;
		n=input;	fwrite(&n,4,1,f);
		n=hidden;	fwrite(&n,4,1,f);
		n=output;	fwrite(&n,4,1,f);
		fwrite(weight,sizeof(weight),1,f);
		return	true;
	}
	bool	load(FILE	*f){
		uint32_t	n;
		if(fread(&n,4,1,f)!=1||n!=input)	return	false;
		if(fread(&n,4,1,f)!=1||n!=hidden)	return	false;
		if(fread(&n,4,1,f)!=1||n!=output)	return	false;
		if(fread(weight,sizeof(weight),1,f)!=1)	return	false;
		return	true;
	}
	void	operator()(float	*x,	float	*y,	float	eta) {
		float	*a1=a+hidden,	*d=a1+hidden,	*d1=d+hidden,	*o=d1+hidden;
		memset(a,0,sizeof(a));
		for(size_t	i=0;	i<=input;	i++){
			float	s=(i<input?x[i]:1)*wi,	*w=weight+i*hidden;
			for(size_t	j=0;	j<hidden;	j++)	a[j]+=s*w[j];
		}
		for(size_t	i=0;	i<hidden;	i++)	a[i]=acti(a[i]);
		for(size_t	i=0;	i<hidden;	i++){
			float	s=0,	*w=weight+(input+1)*hidden+i*hidden;
			for(size_t	j=0;	j<hidden;	j++)	s+=a[j]*w[j];
			a1[i]=s;
		}
		for(size_t	i=0;	i<hidden;	i++)	a1[i]=acti(a1[i]*wh);
		for(size_t	i=0;	i<output;	i++){
			float	s=0,	*w=weight+(input+1)*hidden+hidden*hidden+i*hidden;
			for(size_t	j=0;	j<hidden;	j++)	s+=w[j]*a1[j];
			o[i]=s*wh;
		}
		if(eta<0){	
			for(size_t	i=0;	i<output;	i++)	y[i]=o[i];
			return;	
		}
		for(size_t	i=0;	i<output;	i++){
			float	s=fminf(fmaxf(o[i]-y[i],-1),1)*wh*eta,	*w=weight+(input+1)*hidden+hidden*hidden+i*hidden;
			for(size_t	j=0;	j<hidden;	j++){	d1[j]+=s*w[j];	w[j]-=s*a1[j];	}
		}
		for(size_t	i=0;	i<hidden;	i++){
			float	s=d1[i]*grad(a1[i])*wh,	*w=weight+(input+1)*hidden+i*hidden;
			for(size_t	j=0;	j<hidden;	j++){	d[j]+=s*w[j];	w[j]-=s*a[j];	}
		}
		for(size_t	i=0;	i<hidden;	i++)	d[i]*=grad(a[i]);
		for(size_t	i=0;	i<=input;	i++){
			float	s=(i<input?x[i]:1)*wi,	*w=weight+i*hidden;
			for(size_t	j=0;	j<hidden;	j++)	w[j]-=s*d[j];
		}
	}
};
template<size_t	input,	size_t	hidden,	size_t	output>
uint64_t	tlfn<input,hidden,output>::seed=wyhash64(time(NULL),0);
